home *** CD-ROM | disk | FTP | other *** search
/ NOVA - For the NeXT Workstation / NOVA - For the NeXT Workstation.iso / SourceCode / TextORama / TurboTFCell.m < prev   
Text File  |  1992-12-19  |  7KB  |  256 lines

  1. /* TurboTFCell.m
  2.  *
  3.  *   TurboTFCell is a subclass of TextFieldCell which supports
  4.  *        a) length-watching on a string -- maxLength  [length of 0
  5.  *            indicates that length-watching is not active]
  6.  *        b) auto-jumping to nextText if the maximum is reached
  7.  *        c) acceptsReturn will cause the Return character to be
  8.  *            interpreted literally rather than as a signal to end editing
  9.  * 
  10.  *
  11.  * You may freely copy, distribute, and reuse the code in this example.
  12.  * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
  13.  * fitness for any particular use.
  14.  *
  15.  * Written by:  Sharon Zakhour 
  16.  * Created:  Oct/91
  17.  */
  18.  
  19. #import "TurboTFCell.h"
  20. #import <appkit/Application.h>
  21. #import <appkit/nextstd.h>
  22. #import <strings.h>
  23.  
  24. @implementation TurboTFCell
  25.  
  26. /* In order for the length watching (and auto-jumping since that is 
  27.  * determined by length) to work properly, the character and text
  28.  * filters need to know the identity of the cell they are working with.
  29.  */
  30. static id theCell = NULL;
  31.  
  32. - init
  33. {
  34.     return [self initTextCell:""];
  35. }
  36.  
  37. - initTextCell: (const char*) aString
  38. {
  39.     [super initTextCell:aString];
  40.     
  41.     // Initialize these to the default TextField behavior
  42.     originalText = NULL;
  43.     customTextFilter = (NXTextFilterFunc)lengthFilter;
  44.     [self setAutoJump: NO forLength: 0];
  45.     [self setAcceptsReturn: NO];
  46.     return self;
  47. }
  48.  
  49. - free
  50. {
  51.     if (originalText)
  52.     free(originalText);
  53.     originalText = NULL;
  54.     return[super free];
  55. }
  56.  
  57. /* The primary reason I've overridden select: and edit: (below) is to set
  58.  * the local static variable "theCell" to self.  The filter functions must know
  59.  * about the current cell in order to query it for information on length,
  60.  * autojumping, etc.  The only way that a cell becomes active are
  61.  * through one of these methods.
  62.  */
  63. - select:(const NXRect *)aRect inView:controlView editor:textObj delegate:anObject start:(int)selStart length:(int)selLength
  64. {
  65.     /* do what the superclass would do */
  66.     [super select:aRect inView:controlView editor:textObj 
  67.         delegate:anObject start:selStart length:selLength];
  68.     
  69.     theCell = self;
  70.     if (maxLength && originalText)
  71.         strcpy(originalText, [self stringValue]);
  72.  
  73.     [textObj setTextFilter:(NXTextFilterFunc)customTextFilter];
  74.     [textObj setCharFilter:autoJumpCharFilter];
  75.     return self;
  76. }
  77.  
  78. /* The primary reason I've overridden edit: and select: (above) is to set
  79.  * the local static variable "theCell" to self.  The filter functions must know
  80.  * about the current cell in order to query it for information on length,
  81.  * autojumping, etc.  The only way that a cell becomes active are
  82.  * through one of these methods.
  83.  */
  84. - edit:(const NXRect *)aRect inView:controlView editor:textObj delegate:anObject event:(NXEvent *)theEvent
  85. {
  86.     /* do what the superclass would do */
  87.     [super edit:aRect inView:controlView editor:textObj 
  88.         delegate:anObject event:theEvent];
  89.  
  90.     theCell = self;
  91.     if (maxLength && originalText)
  92.         strcpy(originalText, [self stringValue]);
  93.  
  94.     [textObj setTextFilter:(NXTextFilterFunc)customTextFilter];
  95.     [textObj setCharFilter:autoJumpCharFilter];
  96.     return self;
  97. }
  98.  
  99. - setMaxLength: (int) length
  100. {
  101.     if (originalText)
  102.     {
  103.     free(originalText);
  104.     originalText = NULL;
  105.     }
  106.     maxLength = length;
  107.     if (length)
  108.     originalText = (char *)malloc(maxLength);
  109.     return self;
  110. }
  111.  
  112. - (int) maxLength;
  113. {
  114.     return maxLength;
  115. }
  116.  
  117. - setAutoJump: (BOOL) flag forLength: (int)length
  118. {
  119.     autoJump = flag;
  120.     [self setMaxLength: length];
  121.     return self;
  122. }
  123.  
  124. - (BOOL) autoJump
  125. {
  126.     return autoJump;
  127. }
  128.  
  129. - setAcceptsReturn: (BOOL) flag 
  130. {
  131.     acceptsReturn = flag;
  132.     return self;
  133. }
  134.  
  135. - (BOOL) acceptsReturn
  136. {
  137.     return acceptsReturn;
  138. }
  139.  
  140. - setOriginalText: (char *)aString
  141. {
  142.     if (originalText)
  143.     strcpy(originalText, aString);
  144.     return self;
  145. }
  146.  
  147. - (char *)originalText
  148. {
  149.     return originalText;
  150. }
  151.  
  152. - setCustomFilter: (NXTextFilterFunc) aFilter;
  153. {
  154.     customTextFilter = (NXTextFilterFunc)aFilter;
  155.     return self;
  156. }
  157.  
  158. - (NXTextFilterFunc) customTextFilter
  159. {
  160.     return (NXTextFilterFunc) customTextFilter;
  161. }
  162.  
  163.  
  164. /* Run the given string through the Cell's text filter to 
  165.  * see if it passes muster.
  166.  */
  167. - (BOOL) checkString: (char *)aString
  168. {
  169.     int     i, len;
  170.     char *cp;
  171.     id textObj;
  172.     void *fooPtr;
  173.     
  174.     if (customTextFilter == (NXTextFilterFunc)nil)
  175.         return YES;
  176.     
  177.     textObj = [[[self controlView] window] getFieldEditor :YES for: self];
  178.     for (i = 0, cp = aString; i < strlen(aString); i++, cp++)
  179.     {
  180.         len = 1;
  181.     // fooPtr will eliminate a compiler warning about "unused value"
  182.     fooPtr = (void*)(*(NXTextFilterFunc)customTextFilter)((id)textObj,
  183.         (unsigned char *)cp, (int *)&len, (int)i);
  184.     if (len == 0) 
  185.         return NO;
  186.     }
  187.     return YES;
  188. }
  189.  
  190. // This is the default text filter.  If another has been installed via the setCustomFilter:
  191. // method, then this one will not get called.
  192. char *lengthFilter(id textObj, char *inputText, int *inputLength, int position)
  193. {
  194.     int maxLength;
  195.     BOOL autoJump;
  196.    
  197.     maxLength = [theCell maxLength];
  198.     autoJump = [theCell autoJump];
  199.     
  200.     if (maxLength && !autoJump && [textObj textLength] > maxLength)
  201.     {
  202.     *inputLength = 0;
  203.     return "";
  204.     }
  205.     
  206.     return inputText;
  207. }
  208.  
  209. // This could easily be extended to include letters with diacritics.
  210. // See Chapter 6 Summary for KeyInfo for a full listing of keyboard
  211. // event information.
  212. #define IS_VALID_ASCII(ch)    ((ch) >= ' ' && (ch) <= '~')
  213.  
  214. unsigned short autoJumpCharFilter(unsigned short charCode, int flags, unsigned
  215. short charSet)
  216. {
  217.     NXSelPt     start, end;
  218.     NXEvent    *event, fakeEvent;
  219.     id            textObj;
  220.     int            curLength, maxLength;
  221.     BOOL         autoJump, acceptsReturn;
  222.     
  223.     maxLength = [theCell maxLength];
  224.     autoJump = [theCell autoJump];
  225.     acceptsReturn = [theCell acceptsReturn];
  226.     
  227.     event = [NXApp currentEvent];
  228.     textObj = [[[theCell controlView] window] getFieldEditor :YES for: theCell];
  229.     curLength = [textObj textLength] +1;
  230.     
  231.     /* Anything that's highlighted is going to get clobbered, so let's adjust */
  232.     [textObj getSel: &start : &end];
  233.     if (start.cp != end.cp)
  234.         curLength -= end.cp - start.cp; 
  235.  
  236.     // Currently acceptsReturn doesn't work very well with maxLength because
  237.     // it counts newlines which you probably don't want.  If you want something
  238.     // more sophisticated then you'll have to parse the runs!
  239.     if (acceptsReturn && charCode == NX_CR)
  240.     return NXEditorFilter(charCode, flags, charSet);
  241.     
  242.     // Watch out for interpreted rather than real keystrokes -- let them
  243.     // through even though we have reached our maximum length.
  244.     if (autoJump && maxLength && curLength > maxLength &&
  245.         IS_VALID_ASCII(charCode))
  246.     {
  247.     fakeEvent = *event;
  248.     fakeEvent.time++;
  249.     DPSPostEvent(&fakeEvent, YES);
  250.     return NX_TAB;
  251.     } 
  252.     return NXFieldFilter(charCode, flags, charSet);
  253. }
  254.  
  255. @end
  256.